<!DOCTYPE html>
<meta charset="utf-8">
<title>WebAuthn credential.create() in a cross-origin iframe tests</title>
<meta name="timeout" content="long">
<link rel="help" href="https://w3c.github.io/webauthn/#publickey-credentials-create-feature">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src=helpers.js></script>
<body></body>
<script>
standardSetup(function() {
    "use strict";

    const targetOrigin = "https://{{hosts[alt][www]}}:{{ports[https][0]}}";

    // Returns a |Promise| that gets resolved with |event.data| when |window|
    // receives a "message" event whose |event.data.type| matches the string
    // |message_data_type|.
    function getMessageData(message_data_type) {
        return new Promise(resolve => {
            function waitAndRemove(e) {
                if (!e.data || e.data.type != message_data_type)
                    return;
                window.removeEventListener("message", waitAndRemove);
                resolve(e.data);
            }
            window.addEventListener("message", waitAndRemove);
        });
    }

    // Creates an iframe with the given `src` and (optional) allow attribute.
    // Waits for the iframe to load, based on receiving a "subframe-loaded"
    // message from the iframe.
    async function createIframe(test, src, allow) {
        const iframeElement = document.createElement("iframe");
        document.body.appendChild(iframeElement);
        test.add_cleanup(() => {
            iframeElement.remove();
        });

        if (allow !== undefined) {
          iframeElement.allow = allow;
        }

        const loadedPromise = getMessageData("subframe-loaded");
        iframeElement.src = src;
        await loadedPromise;

        return iframeElement;
    }

    promise_test(async (test) => {
        const src = `${targetOrigin}/webauthn/resources/webauthn-subframe.sub.html`;
        const iframe = await createIframe(test, src);

        const resultPromise = getMessageData("result");
        iframe.contentWindow.postMessage({type: "create-credential"}, {targetOrigin: targetOrigin});
        const data = await resultPromise;

        assert_equals(data.result, "failure");
        assert_equals(data.error.name, "NotAllowedError");
    }, "create() in cross-origin iframe fails without permissions policy");

    promise_test(async (test) => {
        const src = `${targetOrigin}/webauthn/resources/webauthn-subframe.sub.html`;
        const iframe = await createIframe(test, src, "publickey-credentials-create");

        const resultPromise = getMessageData("result");
        iframe.contentWindow.postMessage({type: "create-credential", addUserActivation: false}, {targetOrigin: targetOrigin});
        const data = await resultPromise;

        assert_equals(data.result, "failure");
        assert_equals(data.error.name, "NotAllowedError");
    }, "create() in cross-origin iframe fails with permissions policy but no user activation");

    promise_test(async (test) => {
        const src = `${targetOrigin}/webauthn/resources/webauthn-subframe.sub.html`;
        const iframe = await createIframe(test, src, "publickey-credentials-create");

        const resultPromise = getMessageData("result");
        iframe.contentWindow.postMessage({type: "create-credential", addUserActivation: true}, {targetOrigin: targetOrigin});
        const data = await resultPromise;

        assert_equals(data.result, "success", `Expected success but got error: "${data.errorMessage}"`);
    }, "create() in cross-origin iframe succeeds with permissions policy and user activation");

    promise_test(async (test) => {
        const src = `${targetOrigin}/webauthn/resources/webauthn-subframe.sub.html`;
        const iframe = await createIframe(test, src, "publickey-credentials-create");

        // For this call, we have a user activation in this main frame, but not
        // in the iframe. That shouldn't be sufficient - the user activation has
        // to be on the iframe itself.
        await test_driver.bless("create credential, main frame activation");
        const resultPromise = getMessageData("result");
        iframe.contentWindow.postMessage({type: "create-credential", addUserActivation: false}, {targetOrigin: targetOrigin});
        const data = await resultPromise;

        assert_equals(data.result, "failure");
        assert_equals(data.error.name, "NotAllowedError");
    }, "create() in cross-origin iframe requires user activation on the iframe, not the main frame");
});
</script>

